/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package beans;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

/**
 *
 * @author Asteroidz
 */
@ManagedBean
@SessionScoped
public class ControllerWeb implements Serializable {

    private String selectedDb = "projet_jsf";
    private String selectedTable = null;
    private Connection co = null;
    private DataSource ds;
    private int nbp = 0;
    private int tbcs;
    private int nbpage = 1;

    public LinkedHashMap<String, List<Object>> tbc;
    public LinkedHashMap<String, Object> tbf;
    public LinkedHashMap<String, Object> tbi;
    public LinkedHashMap<String, String> tfk;

    public String errMsg;
    public String mtable;
    public int mid;
    public String maction;

    @PostConstruct
    public void init() {
        this.debug();
    }

/* ~~~~~~~~~~ GETTERS / SETTERS ~~~~~~~~~~ */
    public String getMtable() {
        return mtable;
    }

    public void setMtable(String mtable) {
        this.mtable = mtable;
    }

    public int getMid() {
        return mid;
    }

    public void setMid(int mid) {
        this.mid = mid;
    }

    public String getMaction() {
        return maction;
    }

    public void setMaction(String maction) {
        this.maction = maction;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }

    public int getNbp() {
        return nbp;
    }

    public void setNbp(int nbp) {
        this.nbp = nbp;
    }

    public int getTbcs() {
        return tbcs;
    }

    public void setTbcs(int tbcs) {
        this.tbcs = tbcs;
    }

    public int getNbpage() {
        return nbpage;
    }

    public void setNbpage(int nbpage) {
        this.nbpage = nbpage;
    }
    
    public LinkedHashMap<String, Object> getTbf() {
        return tbf;
    }

    public void setTbf(LinkedHashMap<String, Object> tbf) {
        this.tbf = tbf;
    }

    public LinkedHashMap<String, List<Object>> getTbc() {
        return tbc;
    }

    public void setTbc(LinkedHashMap<String, List<Object>> tbc) {
        this.tbc = tbc;
    }

    public LinkedHashMap<String, Object> getTbi() {
        return tbi;
    }

    public void setTbi(LinkedHashMap<String, Object> tbi) {
        this.tbi = tbi;
    }

    public LinkedHashMap<String, String> getTfk() {
        return tfk;
    }

    public void setTfk(LinkedHashMap<String, String> tfk) {
        this.tfk = tfk;
    }

    public String getSelectedTable() {
        return selectedTable;
    }

    public void setSelectedTable(String selectedTable) {
        this.selectedTable = selectedTable;
    }

    public String getSelectedDb() {
        return selectedDb;
    }

    public void setSelectedDb(String selectedDb) {
        this.selectedDb = selectedDb;
    }

    public DataSource getDs() {
        return ds;
    }

    public void setDs(DataSource ds) {
        this.ds = ds;
    }
    
    public void setCo(Connection co) {
        this.co = co;
    }
    

/* ~~~~~~~~~~ FONCTIONS ~~~~~~~~~~ */
    /**
     * Obtient les paramètres d'url (pour définir l'action et la table)
     */
    public void getRequestedValue() {
        Map<String, String> requestParams = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();

        // on affecte la table
        this.setMtable(requestParams.get("mtable"));

        // on affecte l'id 
        int tempMid;
        try {
            tempMid = Integer.parseInt(requestParams.get("mid"));
            this.setMid(tempMid);
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        // on affecte l'action
        this.setMaction(requestParams.get("maction"));
    }

    /**
     * Créé une connexion MySQL avec le serveur
     *
     * @return -> (Connection) une connexion au serveur
     */
    public Connection getConnection() {
        if (this.co == null) {
            try {
                Context ctx = new InitialContext();
                Context envContext = (Context) ctx.lookup("java:/comp/env");
                if (envContext != null) {
                    ds = (DataSource) envContext.lookup("jdbc/galerie");
                    if (ds != null) {
                        this.co = ds.getConnection();
                    }
                }
            } catch (NamingException | SQLException ex) {
                System.out.print(ex.toString());
            }
        }
        return this.co;
    }

    /**
     * Ferme la connexion au serveur
     */
    public void closeConnection() {
        try {
            this.co.close();
        } catch (SQLException ex) {
            Logger.getLogger(ControllerWeb.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.co = null;
    }

    /**
     * va chercher le nom de toutes les DataBases pour l'affichage
     *
     * @return -> (List:String) une liste de tous les noms de DataBases
     */
    public List<String> getDatabases() {
        this.setCo(this.getConnection());
        MySqlEntity.switchDataBase(this.co, this.selectedDb);
        if (this.selectedTable == null) {
            this.setSelectedTable(MySqlEntity.getFirstTable(this.co));
        }
        List<String> dbs = MySqlEntity.getAllDataBases(this.co);
        this.closeConnection();
        return dbs;
    }

    /**
     * Va appeler une méthode qui permet de changer la DataBase utilisée
     */
    public void switchDatabase() {
        this.setCo(this.getConnection());
        MySqlEntity.switchDataBase(this.co, this.selectedDb);
        this.setSelectedTable(MySqlEntity.getFirstTable(this.co));
        this.closeConnection();
        this.resetPagi();
    }

    public void resetPagi() {
        this.setNbpage(1);
        this.setNbp(0);
    }

    /**
     * Va appeler une methode qui va chercher la description d'une table
     * 
     * @return -> (LinkedHashMap:String, List(Object)) une LinkedHashMap qui contient la description de la table
     */
    public LinkedHashMap<String, List<Object>> getTableDesc() {
        this.setCo(this.getConnection());
        LinkedHashMap<String, List<Object>> tbd = MySqlEntity.getTableDesc(this.co, this.selectedTable);
        this.closeConnection();
        return tbd;
    }

    /**
     * va chercher le nom de toutes les tables d'une DataBase pour l'affichage
     *
     * @return -> (List:String) une liste de tous les noms des tables
     */
    public List<String> getTables() {
        this.setCo(this.getConnection());
        List<String> dbs = MySqlEntity.getAllTables(this.co);
        this.closeConnection();
        this.getTableContent();
        return dbs;
    }

    /**
     * appel la requete qui va chercher le contenu d'une table (pour
     * l'affichage)
     */
    public void getTableContent() {
        this.setCo(this.getConnection());
        this.setTfk(MySqlEntity.getTableForeignKey(this.co, this.selectedDb, this.selectedTable));
        this.setTbc(MySqlEntity.getTableContent(this.co, this.selectedTable, this.nbp));
        this.setTbcs((int) Math.floor(MySqlEntity.getCountTable(this.co, this.selectedTable) / 30));
        this.closeConnection();
    }

    /**
     * Fonction qui va charger le contenu de la ligne que l'on veut modifier,
     * pour l'afficher dans un formulaire
     *
     * @return -> (LinkedHashMap:String, Object) une LinkedHashMap contenant les
     * infos de la ligne sélectionnée
     */
    public LinkedHashMap<String, Object> getContentForm() {
        // va choper les paramètres d'url et les affecter aux variables du dessus
        this.getRequestedValue();

        // Pour chaque ligne de la Map
        LinkedHashMap<String, Object> hm = new LinkedHashMap<>();
        for (Entry<String, List<Object>> entry : this.tbc.entrySet()) {
            // on chope la clef et la valeur associée
            String key = entry.getKey();
            List<Object> value = entry.getValue();

            // si la key n'est pas "Controls", on ajoute à la LinkedHashMap la valeur associée
            Object ins = value.get((int) this.getMid() - 1);
            if (!key.equals("Controls_button_jsf_mod_del")) {
                hm.put(key, ins);
            }
        }
        //on set le tbf juste avant de le return (pour le formulaire)
        this.setTbf(hm);
        return this.getTbf();
    }

    /**
     * Va chercher uniquement le nom des colonnes pour les afficher dans le formulaire d'insertion
     * 
     * @return -> (LinkedHashMap:String, Object) une LinkedHashMap contenant le nom des colonnes
     */
    public LinkedHashMap<String, Object> getInsertForm() {
        LinkedHashMap<String, Object> hm = new LinkedHashMap<>();
        for (Entry<String, List<Object>> entry : this.tbc.entrySet()) {
            //list start @ 0
            String key = entry.getKey();
            String fk = this.tfk.get(key);
            if (!key.equals("Controls_button_jsf_mod_del")) {
                if (fk != null && fk.equals("MUL")) {
                 this.setCo(this.getConnection());
                 List<String> ls = MySqlEntity.getFKcontent(co, this.selectedTable, key);
                 hm.put(key, ls);
                 this.closeConnection();
                 } else {
                hm.put(key, null);
                }
            }
        }
        this.setTbi(hm);
        return this.getTbi();
    }

    /**
     * Va appeler une methode qui update la ligne choisie en BDD
     * 
     * @return -> (String) message d'erreur SQL s'il y en a un
     */
    public String updateContent() {
        this.setCo(this.getConnection());
        this.setErrMsg(MySqlEntity.updateContent(this.co, this.selectedDb, this.selectedTable, this.getTbf(), this.getMid(), this.nbp));
        this.closeConnection();
        this.getTableContent();
        return "index.xhtml?faces-redirect=true";
    }

    /**
     * Va appeler une methode qui insert une nouvelle ligne en BDD
     * 
     * @return -> (String) message d'erreur SQL s'il y en a un
     */
    public String insertContent() {
        this.setCo(this.getConnection());
        this.setErrMsg(MySqlEntity.insertContent(this.co, this.selectedDb, this.selectedTable, this.tbi));
        this.closeConnection();
        this.getTableContent();
        return "index.xhtml?faces-redirect=true";
    }

    /**
     * Va appeler une methode qui supprime une existante ligne en BDD
     * 
     * @return -> (String) message d'erreur SQL s'il y en a un
     */
    public String delete() {
        this.setCo(this.getConnection());
        this.setErrMsg(MySqlEntity.deleteContent(this.co, this.selectedDb, this.selectedTable, this.getTbf(), this.getMid(), this.nbp));
        this.closeConnection();
        this.getTableContent();
        return "index.xhtml?faces-redirect=true";
    }

    /**
     * Correspond à l'annulation du delete, renvoie sur l'index
     * 
     * @return -> (String) message d'erreur SQL s'il y en a un
     */
    public String undelete() {
        return "index.xhtml?faces-redirect=true";
    }

    /**
     * affiche les 30 résultats précédents de la requête
     */
    public void previous30() {
        this.setNbp(this.getNbp() - 30);
        this.setNbpage(this.getNbpage() - 1);
        this.getTableContent();
    }

    /**
     * affiche les 30 résultats suivants de la requête
     */
    public void next30() {
        this.setNbp(this.getNbp() + 30);
        this.setNbpage(this.getNbpage() + 1);
        this.getTableContent();
    }

    /**
     * Fonction qu'on utilise uniquement pour isoler un code en cas de debug
     */
    public void debug() {
        // test de fonction a copier coller ici, appeler cette fonction dans l'init

    }
}
